package com.luffy.page.inteceptor;


import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.luffy.page.config.PageProperty;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.sql.Connection;
import java.util.Properties;

/**
 * 自动分页拦截器实现
 *
 * @author zengjw
 */
@Slf4j
@Intercepts({
        @Signature(method = "query", type = Executor.class, args = {
                MappedStatement.class, Object.class, RowBounds.class,
                ResultHandler.class}),
        @Signature(method = "prepare", type = StatementHandler.class, args = {Connection.class, Integer.class})
})

@RequiredArgsConstructor
public class PageInteceptor implements Interceptor {
    private final PageProperty pageProperty;
    private Long pageSize;

    //当前现实的页数

    private Long currPage;

    //数据库类型

    private String dbType;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object parameter = invocation.getArgs()[1];
        if (invocation.getTarget() instanceof StatementHandler) {
            StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
            //获取statementHandler包装类
            MetaObject metaObjectHandler = SystemMetaObject.forObject(statementHandler);
            //分离代理对象链
            while (metaObjectHandler.hasGetter("h")) {
                Object obj = metaObjectHandler.getValue("h");
                metaObjectHandler = SystemMetaObject.forObject(obj);

            }

            while (metaObjectHandler.hasGetter("target")) {

                Object obj = metaObjectHandler.getValue("target");

                metaObjectHandler = SystemMetaObject.forObject(obj);

            }
//            Configuration configuration = (Configuration) metaObjectHandler.getValue("delegate.configuration");
            MappedStatement mappedStatement = (MappedStatement) metaObjectHandler.getValue("delegate.mappedStatement");

//            MappedStatement mappedStatement=(MappedStatement)invocation.getArgs()[0];
            BoundSql boundSql = mappedStatement.getBoundSql(parameter);
            Object parameterObject = boundSql.getParameterObject();
            if (parameterObject instanceof Page){
                return invocation.proceed();
            }
            //获取查询接口映射的相关信息
            //拦截以.ByPage结尾的请求，分页功能的统一实现
            if (pageProperty.isOpen()) {
                //获取进行数据库操作时管理参数的handler
//                ParameterHandler parameterHandler = (ParameterHandler) MetaObjectHandler.getValue("delegate.parameterHandler");
                //参数名称和在service中设置到map中的名称一致
                currPage = pageProperty.getCurrent();
                if (null==currPage){
                    currPage=1L;
                }
                pageSize = pageProperty.getSize();
                String sql = (String) metaObjectHandler.getValue("delegate.boundSql.sql");
                //也可以通过statementHandler直接获取
                //sql = statementHandler.getBoundSql().getSql();
                //构建分页功能的sql语句
                sql = sql.trim();
                String pageSql = "select * from (select t1.*, rownum rn from (" + sql +
                        ") t1 ) t2 where rn > " + currPage * (pageSize - 1L) + " and rn <= " + currPage * pageSize;
                log.info("分页sql：" + pageSql);
                //将构建完成的分页sql语句赋值个体'delegate.boundSql.sql'，偷天换日
                metaObjectHandler.setValue("delegate.boundSql.sql", pageSql);

            }
        }

        //调用原对象的方法，进入责任链的下一级

        return invocation.proceed();


    }


    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }


}

